﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace JWalsh.XnaSamples.Framework
{
    public class Camera
    {
        private Vector3 _position;
        private Vector3 _lookAt;
        private Matrix _viewMatrix;
        private Matrix _projectionMatrix;
        private float _aspectRatio;

        public Camera(Viewport viewport)
        {
            this._aspectRatio = ((float)viewport.Width) / ((float)viewport.Height);
            this._projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(40.0f),this._aspectRatio,1.0f,10000.0f);
        }

        public Vector3 Position
        {
            get { return this._position; }
            set { this._position = value; }
        }

        public void  MoveCamera (Vector3 movePos)
        {
            _position += movePos;
        }

        public Vector3 LookAt
        {
            get { return this._lookAt; }
            set { this._lookAt = value; }
        }

        public Matrix ViewMatrix
        {
            get { return this._viewMatrix; }
        }
        public Matrix ProjectionMatrix
        {
            get { return this._projectionMatrix; }
        }

        private Vector3 Cross(Vector3 vV1, Vector3 vV2, Vector3 vVector2)
        {
            Vector3 vNormal;
            Vector3 vVector1;
            vVector1.X = vV1.X - vV2.X;
            vVector1.Y = vV1.Y - vV2.Y;
            vVector1.Z = vV1.Z - vV2.Z;

            // Если у нас есть 2 вектора (вектор взгляда и вертикальный вектор), 
            // у нас есть плоскость, от которой мы можем вычислить угол в 90 градусов.
            // Рассчет cross'a прост, но его сложно запомнить с первого раза. 
            // Значение X для вектора = (V1.y * V2.z) - (V1.z * V2.y)
            vNormal.X = ((vVector1.Y * vVector2.Z) - (vVector1.Z * vVector2.Y));

            // Значение Y = (V1.z * V2.x) - (V1.x * V2.z)
            vNormal.Y = ((vVector1.Z * vVector2.X) - (vVector1.X * vVector2.Z));

            // Значение Z = (V1.x * V2.y) - (V1.y * V2.x)
            vNormal.Z = ((vVector1.X * vVector2.Y) - (vVector1.Y * vVector2.X));

            // *ВАЖНО* Вы не можете менять этот порядок, иначе ничего не будет работать.
            // Должно быть именно так, как здесь. Просто запомните, если вы ищите Х, вы не
            // используете значение X двух векторов, и то же самое для Y и Z. Заметьте,
            // вы рассчитываете значение из двух других осей, и никогда из той же самой.

            // Итак, зачем всё это? Нам нужно найти ось, вокруг которой вращаться. Вращение камеры
            // влево и вправо простое - вертикальная ось всегда (0,1,0). 
            // Вращение камеры вверх и вниз отличается, так как оно происходит вне 
            // глобальных осей. Достаньте себе книгу по линейной алгебре, если у вас 
            // её ещё нет, она вам пригодится.

            // вернем результат.
            return vNormal;
        }

        private float Magnitude(Vector3 normal)
        {
            // Это даст нам величину нашей нормали, 
            // т.е. длину вектора. Мы используем эту информацию для нормализации
            // вектора. Вот формула: magnitude = sqrt(V.x^2 + V.y^2 + V.z^2)   где V - вектор.

            return (float)Math.Sqrt(Math.Pow(normal.X ,2) +
                    Math.Pow(normal.Y ,2) +
                    Math.Pow(normal.Z ,2));
        }

        private Vector3 Normalize(Vector3 vector)
        {
            // Вы спросите, для чего эта ф-я? Мы должны убедиться, что наш вектор нормализирован.
            // Вектор нормализирован - значит, его длинна равна 1. Например,
            // вектор (2,0,0) после нормализации будет (1,0,0).

            // Вычислим величину нормали
            float magnitude = Magnitude(vector);

            // Теперь у нас есть величина, и мы можем разделить наш вектор на его величину.
            // Это сделает длинну вектора равной единице, так с ним будет легче работать.
            vector.X = vector.X / magnitude;
            vector.Y = vector.Y / magnitude;
            vector.Z = vector.Z / magnitude;

            return vector;
        }

        public void Update()
        {
            this._viewMatrix =
                Matrix.CreateLookAt(this._position, this._lookAt, Vector3.Up);
        }
    }
}
